﻿using System;
using System.Collections.Generic;
using System.Linq;
using Dorm.Maps;

namespace Dorm.Helpers
{
    public class EntityInfo
    {
        private readonly object _entity;

        public EntityInfo(object entity)
        {
            _entity = entity;
        }

        public long ID
        {
            get { return Convert.ToInt64(GetValue("ID")); }
            set { SetValue("ID", value); }
        }

        public object InnerEntity
        {
            get { return _entity; }
        }

        public Type EntityType
        {
            get { return _entity.GetType(); }
        }

        public string TypeName
        {
            get { return _entity.GetType().Name; }
        }

        public bool IsEntity()
        {
            return HasProperty("ID");
        }

        public bool IsValueObject()
        {
            return !IsEntity();
        }

        public static bool IsValueObject(Type type)
        {
            return type.GetProperty("ID") == null;
        }

        public bool IsValueObject(string propertyName)
        {
            return IsValueObject(GetValue(propertyName).GetType());
        }

        public bool HasProperty(string propertyName)
        {
            return _entity.GetType().GetProperty(propertyName) != null;
        }

        public object GetValue(string propertyName)
        {
            return _entity.GetType().GetProperty(propertyName).GetValue(_entity, null);
        }

        public void SetValue(string propertyName, object value)
        {
            _entity.GetType().GetProperty(propertyName).SetValue(_entity, value, null);
        }

        public bool IsListReferenceType(string propertyName)
        {
            var propertyType = _entity.GetType().GetProperty(propertyName).PropertyType;
            foreach (Type interfaceType in propertyType.GetInterfaces())
            {
                return interfaceType.IsGenericType &&
                       interfaceType.GetGenericTypeDefinition() == typeof(IList<>);
            }
            return false;
        }

        public Type GetSingleType(string listPropertyName)
        {
            var listPropertyType = _entity.GetType().GetProperty(listPropertyName).PropertyType;
            return listPropertyType.GetGenericArguments()[0];
        }

        internal bool IsNew()
        {
            return ID == 0;
        }

        public void BindParentReference(object child)
        {
            // If parent and children are of the same type, look for the parent property
            if (child.GetType().Name == this.TypeName)
            {
                var entityMaps = MapLocator.GetMap(child.GetType());
                string parentName = entityMaps
                            .Where(c => c.MappingType == MappedProperty.MappingTypes.Reference
                                && c.PropertyType == child.GetType())
                                .FirstOrDefault().PropertyName;
                var parentProperty = child.GetType().GetProperty(parentName);
                if (parentProperty != null)
                    parentProperty.SetValue(child, _entity, null);
            }

            // else, just fill the first property of the same type
            foreach (var propertyInfo in child.GetType().GetProperties())
            {
                if (propertyInfo.PropertyType == _entity.GetType())
                {
                    propertyInfo.SetValue(child, _entity, null);
                }
            }
        }
    }
}
